package com.tms.common.core.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis服务类，提供Redis基础操作功能
 */
@Component
public class RedisService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // ============================== 基本操作 ==============================

    /**
     * 存储键值对
     *
     * @param key   键
     * @param value 值
     */
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 存储键值对并设置过期时间
     *
     * @param key     键
     * @param value   值
     * @param timeout 过期时间（秒）
     */
    public void set(String key, Object value, long timeout) {
        redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
    }

    /**
     * 获取值
     *
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 删除键
     *
     * @param key 键
     */
    public void delete(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 批量删除键
     *
     * @param keys 键集合
     */
    public void delete(Collection<String> keys) {
        redisTemplate.delete(keys);
    }

    /**
     * 判断键是否存在
     *
     * @param key 键
     * @return 是否存在
     */
    public boolean hasKey(String key) {
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }

    /**
     * 设置有效时间
     *
     * @param key     Redis键
     * @param timeout 超时时间
     * @param unit    时间单位
     * @return true=设置成功；false=设置失败
     */
    public boolean expire(String key, long timeout, TimeUnit unit) {
        return Boolean.TRUE.equals(redisTemplate.expire(key, timeout, unit));
    }

    /**
     * 设置有效时间（秒）
     *
     * @param key     Redis键
     * @param timeout 超时时间（秒）
     * @return true=设置成功；false=设置失败
     */
    public boolean expire(String key, long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 获取过期时间
     *
     * @param key Redis键
     * @return 过期时间（秒）
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    // ============================== List操作 ==============================

    /**
     * 缓存List数据
     *
     * @param key      缓存的键值
     * @param dataList 待缓存的List数据
     */
    public <T> void setCacheList(String key, List<T> dataList) {
        redisTemplate.opsForList().rightPushAll(key, dataList.toArray());
    }

    /**
     * 获取缓存的List数据
     *
     * @param key 缓存的键值
     * @return 缓存的List数据
     */
    public <T> List<T> getCacheList(String key) {
        return (List<T>) redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 向List中添加一个元素
     *
     * @param key   键
     * @param value 值
     * @return 添加后的长度
     */
    public long listRightPush(String key, Object value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

    /**
     * 从List中获取指定范围的元素
     *
     * @param key   键
     * @param start 开始位置
     * @param end   结束位置
     * @return 元素列表
     */
    public List<Object> listRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 获取List长度
     *
     * @param key 键
     * @return 长度
     */
    public long listSize(String key) {
        return redisTemplate.opsForList().size(key);
    }

    // ============================== Hash操作 ==============================

    /**
     * 设置Hash字段值
     *
     * @param key     键
     * @param hashKey Hash键
     * @param value   值
     */
    public void hashPut(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    /**
     * 批量设置Hash字段值
     *
     * @param key  键
     * @param map  Hash键值对
     */
    public void hashPutAll(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 获取Hash字段值
     *
     * @param key     键
     * @param hashKey Hash键
     * @return 值
     */
    public Object hashGet(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    /**
     * 获取Hash所有字段值
     *
     * @param key 键
     * @return Hash键值对
     */
    public Map<Object, Object> hashGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 删除Hash字段
     *
     * @param key     键
     * @param hashKey Hash键
     * @return 删除的数量
     */
    public long hashDelete(String key, Object... hashKey) {
        return redisTemplate.opsForHash().delete(key, hashKey);
    }

    /**
     * 判断Hash字段是否存在
     *
     * @param key     键
     * @param hashKey Hash键
     * @return 是否存在
     */
    public boolean hashHasKey(String key, String hashKey) {
        return redisTemplate.opsForHash().hasKey(key, hashKey);
    }

    // ============================== Set操作 ==============================

    /**
     * 向Set中添加元素
     *
     * @param key    键
     * @param values 值
     * @return 添加成功的数量
     */
    public long setAdd(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }

    /**
     * 获取Set所有元素
     *
     * @param key 键
     * @return 元素集合
     */
    public Set<Object> setMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 判断Set中是否存在元素
     *
     * @param key   键
     * @param value 值
     * @return 是否存在
     */
    public boolean setIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    /**
     * 获取Set长度
     *
     * @param key 键
     * @return 长度
     */
    public long setSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    /**
     * 从Set中移除元素
     *
     * @param key    键
     * @param values 值
     * @return 移除成功的数量
     */
    public long setRemove(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }

    // ============================== GEO地理位置操作 ==============================

    /**
     * 添加地理位置信息
     *
     * @param key    键
     * @param point  坐标点(经度,纬度)
     * @param member 成员
     * @return 添加的数量
     */
    public Long geoAdd(String key, Point point, String member) {
        return redisTemplate.opsForGeo().add(key, point, member);
    }

    /**
     * 批量添加地理位置信息
     *
     * @param key      键
     * @param memberCoordinateMap 成员坐标映射
     * @return 添加的数量
     */
    public Long geoAdd(String key, Map<Object, Point> memberCoordinateMap) {
        return redisTemplate.opsForGeo().add(key, memberCoordinateMap);
    }

    /**
     * 获取地理位置坐标
     *
     * @param key     键
     * @param members 成员
     * @return 坐标列表
     */
    public List<Point> geoPosition(String key, Object... members) {
        return redisTemplate.opsForGeo().position(key, members);
    }

    /**
     * 计算两个成员之间的距离
     *
     * @param key     键
     * @param member1 成员1
     * @param member2 成员2
     * @param metric  度量单位
     * @return 距离
     */
    public Distance geoDistance(String key, String member1, String member2, Metrics metric) {
        return redisTemplate.opsForGeo().distance(key, member1, member2, metric);
    }

    /**
     * 获取指定范围内的地理位置
     *
     * @param key      键
     * @param circle   圆形范围
     * @param args     命令参数
     * @return 地理位置结果
     */
    public GeoResults<RedisGeoCommands.GeoLocation<Object>> geoRadius(String key, Circle circle, RedisGeoCommands.GeoRadiusCommandArgs args) {
        return redisTemplate.opsForGeo().radius(key, circle, args);
    }

    /**
     * 获取指定成员周围的地理位置
     *
     * @param key      键
     * @param member   成员
     * @param distance 距离
     * @param args     命令参数
     * @return 地理位置结果
     */
    public GeoResults<RedisGeoCommands.GeoLocation<Object>> geoRadiusByMember(String key, Object member, Distance distance, RedisGeoCommands.GeoRadiusCommandArgs args) {
        return redisTemplate.opsForGeo().radius(key, member, distance, args);
    }

    /**
     * 删除地理位置信息
     *
     * @param key     键
     * @param members 成员
     * @return 删除的数量
     */
    public Long geoRemove(String key, Object... members) {
        return redisTemplate.opsForGeo().remove(key, members);
    }

    // ============================== 车辆轨迹专用方法 ==============================

    /**
     * 添加车辆位置点
     *
     * @param vehicleId 车辆ID
     * @param timestamp 时间戳
     * @param longitude 经度
     * @param latitude  纬度
     * @return 是否添加成功
     */
    public boolean addVehiclePosition(String vehicleId, long timestamp, double longitude, double latitude) {
        String key = "vehicle:track:" + vehicleId;
        String member = String.valueOf(timestamp);
        Point point = new Point(longitude, latitude);
        return redisTemplate.opsForGeo().add(key, point, member) > 0;
    }

    /**
     * 获取车辆轨迹
     *
     * @param vehicleId 车辆ID
     * @param startTime 开始时间戳
     * @param endTime   结束时间戳
     * @return 轨迹点列表
     */
    public List<Point> getVehicleTrack(String vehicleId, long startTime, long endTime) {
        String key = "vehicle:track:" + vehicleId;
        // 使用zrange获取指定时间范围内的轨迹点
        Set<Object> members = redisTemplate.opsForZSet().rangeByScore(key, startTime, endTime);
        if (members == null || members.isEmpty()) {
            return List.of();
        }
        return redisTemplate.opsForGeo().position(key, members.toArray());
    }

    /**
     * 设置车辆轨迹过期时间
     *
     * @param vehicleId 车辆ID
     * @param days      保留天数
     * @return 是否设置成功
     */
    public boolean setVehicleTrackExpire(String vehicleId, int days) {
        String key = "vehicle:track:" + vehicleId;
        return expire(key, days * 24 * 60 * 60);
    }

    /**
     * 查找指定范围内的车辆
     *
     * @param longitude 中心点经度
     * @param latitude  中心点纬度
     * @param radius    半径（米）
     * @return 车辆ID列表
     */
    public List<String> findVehiclesNearby(double longitude, double latitude, double radius) {
        String key = "vehicle:current:positions";
        Circle circle = new Circle(new Point(longitude, latitude), new Distance(radius, Metrics.MILES));
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().sortAscending();
        GeoResults<RedisGeoCommands.GeoLocation<Object>> results = redisTemplate.opsForGeo().radius(key, circle, args);

        return results.getContent().stream()
                .map(GeoResult::getContent)
                .map(RedisGeoCommands.GeoLocation::getName)
                .map(Object::toString)
                .toList();
    }

    /**
     * 更新车辆当前位置
     *
     * @param vehicleId 车辆ID
     * @param longitude 经度
     * @param latitude  纬度
     * @return 是否更新成功
     */
    public boolean updateVehicleCurrentPosition(String vehicleId, double longitude, double latitude) {
        String key = "vehicle:current:positions";
        Point point = new Point(longitude, latitude);
        return redisTemplate.opsForGeo().add(key, point, vehicleId) > 0;
    }

    /**
     * 计算两车之间的距离
     *
     * @param vehicleId1 车辆1 ID
     * @param vehicleId2 车辆2 ID
     * @return 距离（米）
     */
    public Double calculateDistanceBetweenVehicles(String vehicleId1, String vehicleId2) {
        String key = "vehicle:current:positions";
        Distance distance = redisTemplate.opsForGeo().distance(key, vehicleId1, vehicleId2, Metrics.MILES);
        return distance != null ? distance.getValue() : null;
    }
}